import * as path from 'path';
import * as fs from 'fs';
import { Logger } from '../../../src/logger/Logger';
import { getInferenceEngine } from '../../../core/inference/OpenHarmonyInferenceEngine';

/**
 * OpenHarmony 单元测试生成器
 * 基于 ohcode/filemanagement_user_file_service 项目的规范
 * 
 * 集成智能推理引擎，提升 GN 配置准确性
 */
export class OpenHarmonyUTGenerator {
    private static inferenceEngine = getInferenceEngine();
    
    /**
     * 从 OHCode 目录查找并学习 unittest 示例
     */
    public static learnUnittestExamples(projectRoot: string): string {
        try {
            // 尝试找到 test/unittests 或 test/unittest 目录
            const possibleTestDirs = [
                path.join(projectRoot, 'test', 'unittests'),
                path.join(projectRoot, 'test', 'unittest'),
                path.join(projectRoot, 'test')
            ];

            let unittestDir: string | null = null;
            for (const dir of possibleTestDirs) {
                if (fs.existsSync(dir)) {
                    unittestDir = dir;
                    Logger.info(`[UT Generator] 找到 unittest 目录: ${dir}`);
                    break;
                }
            }

            if (!unittestDir) {
                Logger.info(`[UT Generator] 未找到 unittest 目录`);
                return '';
            }

            // 读取第一个找到的测试 BUILD.gn 作为示例
            let exampleBuildGn = '';
            let exampleTestCpp = '';

            const readDirRecursive = (dir: string, depth: number = 0): void => {
                if (depth > 2 || exampleBuildGn || exampleTestCpp) return;

                try {
                    const files = fs.readdirSync(dir);
                    for (const file of files) {
                        const filePath = path.join(dir, file);
                        const stat = fs.statSync(filePath);

                        if (stat.isDirectory()) {
                            readDirRecursive(filePath, depth + 1);
                        } else if (file === 'BUILD.gn' && !exampleBuildGn) {
                            exampleBuildGn = fs.readFileSync(filePath, 'utf-8');
                            Logger.info(`[UT Generator] 读取示例 BUILD.gn: ${filePath}`);
                        } else if (file.endsWith('_test.cpp') && !exampleTestCpp) {
                            const content = fs.readFileSync(filePath, 'utf-8');
                            // 截取前 100 行作为示例
                            exampleTestCpp = content.split('\n').slice(0, 100).join('\n');
                            Logger.info(`[UT Generator] 读取示例测试文件: ${filePath}`);
                        }

                        if (exampleBuildGn && exampleTestCpp) break;
                    }
                } catch (error) {
                    // 忽略权限错误等
                }
            };

            readDirRecursive(unittestDir);

            if (!exampleBuildGn && !exampleTestCpp) {
                return '';
            }

            // 构建学习资料
            let learnPrompt = `\n\n## 📚 项目 Unittest 示例参考\n\n`;
            
            if (exampleBuildGn) {
                learnPrompt += `### 📋 项目 unittest BUILD.gn 示例\n\`\`\`gn\n${exampleBuildGn.substring(0, 2000)}\n\`\`\`\n\n`;
                learnPrompt += `**BUILD.gn 关键规则**：\n`;
                learnPrompt += `- 使用 \`ohos_unittest("xxx_test")\` 定义测试\n`;
                learnPrompt += `- module_out_path 格式：\`"subsystem/module"\`\n`;
                learnPrompt += `- external_deps 必须包含：\`"googletest:gmock_main"\`, \`"googletest:gtest_main"\`\n`;
                learnPrompt += `- 常用 defines：\`"private=public"\` (用于测试私有方法)\n`;
                learnPrompt += `- 使用 \`use_exceptions = true\`\n`;
                learnPrompt += `- group("xxx_test") 包含所有测试目标\n\n`;
            }

            if (exampleTestCpp) {
                learnPrompt += `### ✅ 项目 unittest CPP 示例\n\`\`\`cpp\n${exampleTestCpp}\n...\n\`\`\`\n\n`;
                learnPrompt += `**测试代码关键规则**：\n`;
                learnPrompt += `- 继承 \`testing::Test\`\n`;
                learnPrompt += `- 实现 SetUpTestCase/TearDownTestCase/SetUp/TearDown\n`;
                learnPrompt += `- 使用 \`HWTEST_F(TestClass, TestName, TestSize.Level0)\`\n`;
                learnPrompt += `- 测试用例注释格式：@tc.name, @tc.desc, @tc.type, @tc.require\n`;
                learnPrompt += `- 使用 EXPECT_*/ASSERT_* 进行断言\n\n`;
            }

            return learnPrompt;
        } catch (error) {
            Logger.error(`[UT Generator] 学习 unittest 示例失败: ${error}`);
            return '';
        }
    }
    
    /**
     * 构建符合 OpenHarmony 规范的测试生成 Prompt
     */
    public static buildOHTestGenerationPrompt(
        functionName: string,
        functionCode: string,
        sourceFilePath: string,
        className?: string,
        returnType?: string
    ): string {
        // 分析源文件的头文件依赖
        const sourceIncludes = this.extractIncludesFromSource(sourceFilePath);
        const mainHeader = this.extractMainHeader(sourceFilePath);
        
        // 尝试学习项目的 unittest 示例
        const projectRoot = this.findProjectRoot(sourceFilePath);
        const unittestLearning = projectRoot ? this.learnUnittestExamples(projectRoot) : '';
        
        // ⭐ 从知识库检索相关示例
        let knowledgeBaseExamples = '';
        try {
            const { KnowledgeRetriever } = require('../../../core/knowledge/KnowledgeRetriever');
            const retriever = new KnowledgeRetriever();
            if (retriever.loadKnowledgeBase()) {
                // 检索单元测试示例
                knowledgeBaseExamples += retriever.retrieveUnitTestExamples({
                    functionName,
                    className,
                    sourceFilePath
                });
                
                // 检索 BUILD.gn 配置
                knowledgeBaseExamples += retriever.retrieveBuildConfigExamples({
                    testType: 'unittest'
                });
                
                // 检索 Mock 模式
                knowledgeBaseExamples += retriever.retrieveMockPatterns();
                
                console.log('[OpenHarmonyUTGenerator] ✅ 已从知识库检索到学习示例');
            }
        } catch (error) {
            console.warn('[OpenHarmonyUTGenerator] ⚠️  知识库加载失败，使用默认模板:', error);
        }
        
        const currentYear = new Date().getFullYear();
        
        return `你是一个专业的 OpenHarmony C++ 单元测试生成专家。请为以下 C++ 函数生成完整且符合 OpenHarmony 规范的单元测试代码。

## 源代码信息
**函数名称**: ${functionName}
${className ? `**类名**: ${className}` : ''}
${returnType ? `**返回类型**: ${returnType}` : ''}
**主头文件**: ${mainHeader}

**源代码**:
\`\`\`cpp
${functionCode}
\`\`\`

**源文件的依赖**:
${sourceIncludes.slice(0, 10).join('\n')}
${unittestLearning}

## OpenHarmony 单元测试规范

### 0. 版权信息（必须放在文件最开头）
\`\`\`cpp
/*
 * Copyright (c) ${currentYear} Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
\`\`\`
**❌ 禁止使用其他年份（如 2022、2023）！必须使用 ${currentYear} 年！**

### 1. 头文件包含顺序（严格按此顺序）
\`\`\`cpp
// 1. 主要被测类的头文件（第一个，必须）
#include "${mainHeader}"

// 2. gtest/gmock 测试框架（必须）
#include <gtest/gtest.h>
#include <gmock/gmock.h>

// 3. 被测类的依赖头文件（从源文件中提取关键依赖）
// 例如：
// #include "notify_work_service.h"
// #include "system_ability_definition.h"
\`\`\`

### 2. 命名空间和 using 声明（必须）
\`\`\`cpp
using namespace testing;
using namespace testing::ext;
using namespace OHOS::FileAccessFwk;
using namespace OHOS::FileManagement;

namespace OHOS {
namespace FileAccessFwk {
\`\`\`

### 3. 测试类结构（标准格式）
\`\`\`cpp
class ${className || functionName}Test : public testing::Test {
public:
    static void SetUpTestCase(void);
    static void TearDownTestCase(void);
    void SetUp();
    void TearDown();
    
    // 测试需要的成员变量
    ${className ? `std::shared_ptr<${className}> instance_;` : '// 根据需要添加'}
};

void ${className || functionName}Test::SetUpTestCase(void)
{
    // 所有测试开始前执行一次
}

void ${className || functionName}Test::TearDownTestCase(void)
{
    // 所有测试结束后执行一次
}

void ${className || functionName}Test::SetUp()
{
    ${className ? `instance_ = std::make_shared<${className}>();` : '// 初始化代码'}
    GTEST_LOG_(INFO) << "SetUp";
}

void ${className || functionName}Test::TearDown()
{
    GTEST_LOG_(INFO) << "TearDown";
}
\`\`\`

### 4. 测试用例格式（标准格式）
\`\`\`cpp
/**
 * @tc.number: ${className || functionName}_${functionName}_001
 * @tc.name: ${functionName}
 * @tc.desc: Verify ${functionName} function works correctly
 */
HWTEST_F(${className || functionName}Test, ${className || functionName}_${functionName}_001, TestSize.Level1)
{
    GTEST_LOG_(INFO) << "${className || functionName}Test-begin ${className || functionName}_${functionName}_001";
    
    // 测试代码
    ${className ? `ASSERT_NE(instance_, nullptr);` : ''}
    
    // 添加具体的测试逻辑
    
    GTEST_LOG_(INFO) << "${className || functionName}Test-end ${className || functionName}_${functionName}_001";
}
\`\`\`

### 5. 命名规范
- 测试类名：\`{ModuleName}Test\`
- 测试用例名：\`{ModuleName}_{FunctionName}_{序号}\`
- 用例编号：\`@tc.number: {ModuleName}_{FunctionName}_{序号}\`

## 要求

### 📋 基础规范要求
1. **严格遵守上述头文件顺序**，不要遗漏 gtest/gmock
2. 必须包含 \`using namespace testing;\` 等命名空间声明
3. 测试类必须继承 \`testing::Test\`
4. 必须实现 SetUpTestCase、TearDownTestCase、SetUp、TearDown 四个方法
5. 使用 \`HWTEST_F\` 宏定义测试用例
6. 测试用例开始和结束都要用 \`GTEST_LOG_(INFO)\` 记录日志
7. 包含 \`@tc.number\`、\`@tc.name\`、\`@tc.desc\` 注释
8. **分析源文件的依赖，在测试文件中包含必要的依赖头文件**
9. 结束时添加正确的命名空间闭合：\`} // namespace FileAccessFwk\n} // namespace OHOS\`

### 🎯 代码覆盖率要求（核心重点）
**目标：实现最高的代码分支覆盖率，确保所有代码路径都被测试到！**

#### 1. 分支覆盖策略
- **if/else 分支**：必须测试 true 和 false 两种情况
  \`\`\`cpp
  // 示例：if (ptr == nullptr)
  HWTEST_F(Test, NullPtrCase, Level1) {
      // 测试 ptr == nullptr 的情况
  }
  HWTEST_F(Test, ValidPtrCase, Level1) {
      // 测试 ptr != nullptr 的情况
  }
  \`\`\`

- **switch/case 分支**：必须覆盖所有 case 和 default
- **循环分支**：测试 0 次、1 次、多次、边界值
- **逻辑运算符**：测试短路情况（&&, ||）
  \`\`\`cpp
  // 示例：if (a && b)
  HWTEST_F(Test, BothTrue, Level1) { /* a=true, b=true */ }
  HWTEST_F(Test, FirstFalse, Level1) { /* a=false, b=任意 */ }
  HWTEST_F(Test, SecondFalse, Level1) { /* a=true, b=false */ }
  \`\`\`

- **三元表达式**：测试 ? 和 : 两侧
- **提前返回**：测试所有 return 路径

#### 2. 边界值测试（必须）
- **空值/NULL**：nullptr、空字符串、空容器
- **边界值**：0、-1、INT_MAX、INT_MIN
- **特殊值**：负数、浮点数的 NaN/Inf
- **数组边界**：第一个、最后一个、越界

#### 3. 异常路径测试（必须）
- **错误返回**：测试函数返回错误码的情况
- **异常抛出**：使用 \`EXPECT_THROW\` 测试异常
- **资源不足**：内存分配失败、文件打开失败
- **并发冲突**：多线程竞态条件

#### 4. 正常流程测试
- **典型场景**：最常见的正常使用情况
- **多步骤流程**：按正确顺序调用的完整流程

### 📊 测试用例数量指导
- **简单函数**（< 10 行，无分支）：至少 **3 个测试用例**
  - 1 个正常情况
  - 1 个边界情况
  - 1 个异常情况
  
- **中等函数**（10-50 行，少量分支）：至少 **5-8 个测试用例**
  - 覆盖所有 if/else 分支
  - 测试所有边界值
  - 测试错误处理
  
- **复杂函数**（> 50 行，多个分支）：至少 **10+ 个测试用例**
  - 覆盖所有代码路径
  - 组合测试多个条件
  - 覆盖所有异常场景

### 💡 高质量测试示例
\`\`\`cpp
/**
 * @tc.number: ProcessData_001
 * @tc.name: ProcessData_NormalCase
 * @tc.desc: 测试正常数据处理流程
 */
HWTEST_F(MyTest, ProcessData_001, TestSize.Level1)
{
    GTEST_LOG_(INFO) << "MyTest-begin ProcessData_001";
    
    // Arrange: 准备正常数据
    std::vector<int> data = {1, 2, 3, 4, 5};
    
    // Act: 调用被测函数
    int result = instance_->ProcessData(data);
    
    // Assert: 验证结果
    EXPECT_EQ(result, 15);  // 期望结果
    EXPECT_FALSE(data.empty());  // 验证副作用
    
    GTEST_LOG_(INFO) << "MyTest-end ProcessData_001";
}

/**
 * @tc.number: ProcessData_002
 * @tc.name: ProcessData_EmptyInput
 * @tc.desc: 测试空输入的边界情况
 */
HWTEST_F(MyTest, ProcessData_002, TestSize.Level1)
{
    GTEST_LOG_(INFO) << "MyTest-begin ProcessData_002";
    
    // Arrange: 准备空数据
    std::vector<int> data;
    
    // Act & Assert: 验证异常处理
    int result = instance_->ProcessData(data);
    EXPECT_EQ(result, 0);  // 期望返回错误码或默认值
    
    GTEST_LOG_(INFO) << "MyTest-end ProcessData_002";
}

/**
 * @tc.number: ProcessData_003
 * @tc.name: ProcessData_NullPointer
 * @tc.desc: 测试空指针的异常情况
 */
HWTEST_F(MyTest, ProcessData_003, TestSize.Level1)
{
    GTEST_LOG_(INFO) << "MyTest-begin ProcessData_003";
    
    // Act & Assert: 验证空指针保护
    EXPECT_EQ(instance_->ProcessData(nullptr), -1);  // 期望返回错误
    // 或者: EXPECT_THROW(instance_->ProcessData(nullptr), std::invalid_argument);
    
    GTEST_LOG_(INFO) << "MyTest-end ProcessData_003";
}
\`\`\`

### ⚠️ 注意事项
1. **分析源代码的所有分支**：仔细阅读源代码，识别所有 if、else、switch、循环、return 语句
2. **使用 Mock 对象**：当依赖外部服务时，使用 gmock 创建 Mock 对象
3. **独立性**：每个测试用例应该独立，不依赖其他测试的执行顺序
4. **可读性**：测试代码应清晰易懂，使用有意义的变量名和注释
5. **断言充分**：不仅测试返回值，还要验证对象状态、副作用、错误码等

### 🎯 最终目标
**生成的测试代码应该能够实现以下覆盖率指标：**
- **行覆盖率**：≥ 90%
- **分支覆盖率**：≥ 85%
- **函数覆盖率**：100%

请仔细分析源代码，识别所有可能的执行路径，为每个路径生成对应的测试用例。

---

请直接输出完整的测试代码，不要有额外的解释。确保代码可以直接编译运行。`
        + knowledgeBaseExamples;  // ⭐ 追加知识库学到的示例
    }
    
    /**
     * 查找项目根目录（包含 bundle.json 的目录）
     */
    private static findProjectRoot(sourceFilePath: string): string | null {
        let currentDir = path.dirname(sourceFilePath);
        let depth = 0;
        
        while (depth < 10) {
            const bundleJsonPath = path.join(currentDir, 'bundle.json');
            if (fs.existsSync(bundleJsonPath)) {
                Logger.info(`[UT Generator] 找到项目根目录: ${currentDir}`);
                return currentDir;
            }
            
            const parentDir = path.dirname(currentDir);
            if (parentDir === currentDir) {
                break;
            }
            
            currentDir = parentDir;
            depth++;
        }
        
        Logger.info(`[UT Generator] 未找到项目根目录（bundle.json）`);
        return null;
    }
    
    /**
     * 从源文件中提取 #include 语句
     */
    private static extractIncludesFromSource(sourceFilePath: string): string[] {
        try {
            const content = fs.readFileSync(sourceFilePath, 'utf-8');
            const lines = content.split('\n');
            const includes: string[] = [];
            
            for (const line of lines) {
                const trimmed = line.trim();
                if (trimmed.startsWith('#include')) {
                    includes.push(trimmed);
                }
                // 通常头文件都在文件开头，遇到其他代码就停止
                if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('//') && !trimmed.startsWith('/*')) {
                    if (includes.length > 0) break;
                }
            }
            
            return includes;
        } catch (error) {
            Logger.warn(`提取源文件头文件失败: ${error}`);
            return [];
        }
    }
    
    /**
     * 提取主头文件（第一个被包含的自定义头文件）
     */
    private static extractMainHeader(sourceFilePath: string): string {
        const includes = this.extractIncludesFromSource(sourceFilePath);
        
        // 查找第一个使用双引号的 include（通常是主头文件）
        for (const include of includes) {
            const match = include.match(/#include\s+"([^"]+)"/);
            if (match) {
                return match[1];
            }
        }
        
        // 如果没找到，使用文件名推测
        const baseName = path.basename(sourceFilePath, path.extname(sourceFilePath));
        return `${baseName}.h`;
    }
    
    /**
     * 生成标准的 ohos_unittest GN 配置
     */
    public static generateOHGNConfig(
        testName: string,
        testFileName: string,
        projectPath: string,
        existingConfig?: string
    ): string {
        // 提取项目路径变量名（如 user_file_service_path）
        const projectVarName = this.extractProjectVarName(existingConfig || '');
        const pathVar = projectVarName || 'project_path';
        
        return `
ohos_unittest("${testName}") {
  branch_protector_ret = "pac_ret"
  sanitize = {
    integer_overflow = true
    cfi = true
    cfi_cross_dso = true
    debug = false
    blocklist = "\${${pathVar}}/cfi_blocklist.txt"
  }
  module_out_path = "${this.extractModulePath(projectPath)}/${this.extractModulePath(projectPath)}"

  include_dirs = [
    "\${${pathVar}}/interfaces/inner_api/file_access/include",
    "\${${pathVar}}/services/native/file_access_service/include",
    "\${${pathVar}}/utils",
    "mock",
  ]

  sources = [
    "${testFileName}",
  ]

  deps = [
    "\${${pathVar}}/services:file_access_service",
    "\${${pathVar}}/services:file_access_service_base_source",
    "\${${pathVar}}/interfaces/inner_api/file_access:file_access_ext_base_include",
    "\${${pathVar}}/interfaces/inner_api/file_access:file_access_extension_ability_kit",
  ]

  external_deps = [
    "ability_base:want",
    "ability_base:zuri",
    "ability_runtime:app_context",
    "ability_runtime:extensionkit_native",
    "ability_runtime:napi_common",
    "ability_runtime:runtime",
    "access_token:libaccesstoken_sdk",
    "bundle_framework:appexecfwk_core",
    "c_utils:utils",
    "file_api:filemgmt_libn",
    "googletest:gmock_main",
    "googletest:gtest_main",
    "hilog:libhilog",
    "hitrace:hitrace_meter",
    "init:libbegetutil",
    "ipc:ipc_core",
    "ipc:ipc_napi",
    "napi:ace_napi",
    "os_account:os_account_innerkits",
    "safwk:system_ability_fwk",
    "relational_store:native_rdb",
  ]

  defines = [
    "private=public",
    "protected=public"
  ]

  use_exceptions = true
}`;
    }
    
    /**
     * 提取项目路径变量名
     */
    private static extractProjectVarName(gnContent: string): string | null {
        // 查找类似 ${user_file_service_path} 的变量
        const match = gnContent.match(/\$\{([^}]+_path)\}/);
        return match ? match[1] : null;
    }
    
    /**
     * 提取模块路径
     */
    private static extractModulePath(projectPath: string): string {
        // 从路径中提取模块名，如 filemanagement_user_file_service -> user_file_service
        const parts = projectPath.split(/[\/\\]/);
        const lastPart = parts[parts.length - 1] || parts[parts.length - 2];
        
        // 尝试提取 xxx_xxx 格式
        const match = lastPart.match(/[a-z]+_([a-z_]+)/);
        return match ? match[1] : lastPart;
    }
    
    /**
     * 从已有的测试文件中学习 GN 配置模式
     */
    public static async learnGNConfigFromExisting(gnFilePath: string): Promise<{
        projectVarName: string;
        modulePath: string;
        includeDirs: string[];
        deps: string[];
        externalDeps: string[];
        standardConfig: any;
    } | null> {
        try {
            const content = fs.readFileSync(gnFilePath, 'utf-8');
            
            // 提取一个示例测试目标的配置
            const testTargetMatch = content.match(/ohos_unittest\([^)]+\)\s*\{([^}]+(?:\{[^}]+\}[^}]*)*)\}/s);
            if (!testTargetMatch) {
                return null;
            }
            
            const configBlock = testTargetMatch[1];
            
            // 提取各个配置项
            const projectVarName = this.extractProjectVarName(content) || 'project_path';
            const modulePath = this.extractModulePathFromGN(configBlock);
            const includeDirs = this.extractIncludeDirsFromGN(configBlock);
            const deps = this.extractDepsFromGN(configBlock);
            const externalDeps = this.extractExternalDepsFromGN(configBlock);
            
            return {
                projectVarName,
                modulePath,
                includeDirs,
                deps,
                externalDeps,
                standardConfig: {
                    branch_protector_ret: 'pac_ret',
                    sanitize: {
                        integer_overflow: true,
                        cfi: true,
                        cfi_cross_dso: true,
                        debug: false
                    }
                }
            };
        } catch (error) {
            Logger.warn(`从现有 GN 配置学习失败: ${error}`);
            return null;
        }
    }
    
    private static extractModulePathFromGN(configBlock: string): string {
        const match = configBlock.match(/module_out_path\s*=\s*"([^"]+)"/);
        return match ? match[1] : 'module/module';
    }
    
    private static extractIncludeDirsFromGN(configBlock: string): string[] {
        const match = configBlock.match(/include_dirs\s*=\s*\[([^\]]+)\]/s);
        if (!match) return [];
        
        const dirs: string[] = [];
        const dirMatches = match[1].matchAll(/"([^"]+)"/g);
        for (const m of dirMatches) {
            dirs.push(m[1]);
        }
        return dirs;
    }
    
    private static extractDepsFromGN(configBlock: string): string[] {
        const match = configBlock.match(/deps\s*=\s*\[([^\]]+)\]/s);
        if (!match) return [];
        
        const deps: string[] = [];
        const depMatches = match[1].matchAll(/"([^"]+)"/g);
        for (const m of depMatches) {
            deps.push(m[1]);
        }
        return deps;
    }
    
    private static extractExternalDepsFromGN(configBlock: string): string[] {
        const match = configBlock.match(/external_deps\s*=\s*\[([^\]]+)\]/s);
        if (!match) return [];
        
        const deps: string[] = [];
        const depMatches = match[1].matchAll(/"([^"]+)"/g);
        for (const m of depMatches) {
            deps.push(m[1]);
        }
        return deps;
    }
}

